#include "edit.h"

#pragma comment(lib, "gdi32.lib")

namespace edit {
    Editor::Editor() {
        
    }

    Editor::~Editor() {

    }

    int Editor::Init(HINSTANCE hInstance, HWND parent) {
        // 初始化状态
        lines_.push_back("");

        // 注册editor窗口类
        WNDCLASSEXW wc = { 0 };
        wc.cbSize = sizeof(wc);
        wc.lpfnWndProc = Editor::WndProc;
        wc.hInstance = hInstance;
        wc.lpszClassName = _className;
        wc.style = CS_GLOBALCLASS | CS_HREDRAW | CS_VREDRAW;//CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_GLOBALCLASS; // 启用输入事件
        wc.cbWndExtra = sizeof(Editor*);

        if(!::RegisterClassExW(&wc)) {
            return 0;
        }

        _hSelf = ::CreateWindowW(
            // WS_EX_ACCEPTFILES | WS_EX_LAYOUTRTL,
            _className,
            L"aeditor",
            (WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL),
            0, 0, 800, 600,
            parent, (HMENU)IDC_GuiID, hInstance,
            (LPVOID)this
        );

        this->initFontInfo();
        // 显式设置焦点到子窗口
        ::SetFocus(_hSelf);
        return 1;
    }

    LRESULT CALLBACK Editor::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        if (hwnd == NULL) {
            return FALSE;
        }

        switch (msg) {
            case WM_NCCREATE:
            {
                Editor *ide = static_cast<Editor*>((reinterpret_cast<LPCREATESTRUCT>(lParam))->lpCreateParams);
                ide->_hSelf = hwnd;
                ::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(ide));
                return TRUE; 
            }
            default:
            {
                return (reinterpret_cast<Editor*>(::GetWindowLongPtr(hwnd, GWLP_USERDATA))->runProc(hwnd, msg, wParam, lParam));
            }
        }
    }

    LRESULT CALLBACK Editor::runProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
        switch (msg) {
            case WM_GETDLGCODE: {
                // if(lParam)
                // {
                //     LPMSG lpmsg = (LPMSG)lParam;
                //     if( lpmsg->message == WM_CHAR)
                //     {
                //         return DLGC_WANTCHARS;
                //     }
                // }
                // MessageBoxA(hwnd, "WM_GETDLGCODE received!", "Debug", MB_OK);
                return DLGC_WANTALLKEYS;
            }
            case WM_CREATE:
                this->updateScrollBars(hwnd);
                return 0;
            case WM_SIZE:
                this->updateScrollBars(hwnd);
                return 0;
            case WM_LBUTTONDOWN:
                ::SetFocus(hwnd);
                return 0;
            case WM_SETCURSOR: {
                if (LOWORD(lParam) == HTCLIENT) { // 仅修改客户区光标
                    HCURSOR hCursor = LoadCursor(NULL, IDC_IBEAM); // 手型光标
                    ::SetCursor(hCursor);
                    return TRUE; // 表示已处理
                }
                break;
            }
            case WM_VSCROLL: {
                SCROLLINFO si = { sizeof(si), SIF_ALL };
                ::GetScrollInfo(hwnd, SB_VERT, &si);

                int newPos = si.nPos;
                switch (LOWORD(wParam)) {
                    case SB_LINEUP: newPos -= 1; break;
                    case SB_LINEDOWN: newPos += 1; break;
                    case SB_PAGEUP: newPos -= si.nPage; break;
                    case SB_PAGEDOWN: newPos += si.nPage; break;
                    case SB_THUMBTRACK: newPos = si.nTrackPos; break;
                }

                newPos = max(0, min(newPos, si.nMax - (int)si.nPage + 1));
                if (newPos != this->scrollY_) {
                    this->scrollY_ = newPos;
                    ::SetScrollPos(hwnd, SB_VERT, newPos, TRUE);
                    ::InvalidateRect(hwnd, nullptr, TRUE);
                }
                return 0;
            }
            case WM_PAINT:
                this->paintWindow(hwnd);
                return 0;
            case WM_CHAR:
                // MessageBoxA(hwnd, "WM_CHAR received!", "Debug", MB_OK);
                this->handleChar(hwnd, wParam);
                return 0;
            case WM_KEYDOWN:
                // MessageBoxA(hwnd, "WM_KEYDOWN received!", "Debug", MB_OK);
                this->handleKeyDown(hwnd, wParam);
                return 0;
            case WM_TIMER: {
                if (wParam == 1) {
                    this->cursorVisible_ = !this->cursorVisible_;
                    RECT rc;
                    ::GetClientRect(hwnd, &rc);
                    ::InvalidateRect(hwnd, &rc, FALSE);
                }
                return 0;
            }
            case WM_ERASEBKGND:
                return 1; // 阻止擦除背景
            default:
                return ::DefWindowProc(hwnd, msg, wParam, lParam);
        }
    }

    void Editor::paintWindow(HWND hwnd) {
        PAINTSTRUCT ps;
        HDC hdc = ::BeginPaint(hwnd, &ps);
        
        HDC hdcMem = ::CreateCompatibleDC(hdc);
        HBITMAP hbmMem = ::CreateCompatibleBitmap(hdc, ps.rcPaint.right - ps.rcPaint.left, ps.rcPaint.bottom - ps.rcPaint.top);
        HBITMAP hbmOld = (HBITMAP)::SelectObject(hdcMem, hbmMem);

        HBRUSH hBrush = ::CreateSolidBrush(RGB(31, 31, 31)); // 红色画笔
        // 绘制背景 (仅在需要时填充背景)
        // ::FillRect(hdcMem, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
        ::FillRect(hdcMem, &ps.rcPaint, hBrush);

        HFONT hFont = ::CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
            DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS,
            CLEARTYPE_QUALITY, FIXED_PITCH, TEXT("Consolas"));

        ::SelectObject(hdcMem, hFont);
        ::SetBkMode(hdcMem, TRANSPARENT);

        RECT rc;
        ::GetClientRect(hwnd, &rc);
        
        // 绘制文本
        int startLine = this->scrollY_;
        int endLine = min(startLine + rc.bottom  / this->charHeight_ + 1, (int)this->lines_.size());

        for (int y = startLine; y < endLine; y++) {
            int screenY = (y - startLine) * this->charHeight_;
            this->drawTextLine(hdcMem, y, -this->scrollX_ * this->charWidth_, screenY);
        }

        // 绘制光标
        if (this->cursorVisible_) {
            int cursorScreenX = (this->cursorX_ - this->scrollX_) * this->charWidth_;
            int cursorScreenY = (this->cursorY_ - startLine) * this->charHeight_;
            if (cursorScreenY >= 0 && cursorScreenY < rc.bottom) {
                ::PatBlt(hdcMem, cursorScreenX, cursorScreenY, 2, this->charHeight_, DSTINVERT);
            }
        }
        
        // 复制到屏幕
        ::BitBlt(hdc, 0, 0, ps.rcPaint.right, ps.rcPaint.bottom, hdcMem, 0, 0, SRCCOPY);

        ::DeleteObject(hbmMem);
        ::DeleteDC(hdcMem);
        ::DeleteObject(hFont);
        ::EndPaint(hwnd, &ps);
    }

    void Editor::handleChar(HWND hwnd, WPARAM wParam) {
        char ch = (char)wParam;

        if (ch == '\r') { // 回车
            std::string left = this->lines_[this->cursorY_].substr(0, this->cursorX_);
            std::string right = this->lines_[this->cursorY_].substr(this->cursorX_);
            this->lines_[this->cursorY_] = left;
            this->lines_.insert(this->lines_.begin() + this->cursorY_ + 1, right);
            this->cursorY_++;
            this->cursorX_ = 0;
        } else if (ch == '\t') {
            this->lines_[this->cursorY_].insert(this->cursorX_++, 1, ' ');
            this->lines_[this->cursorY_].insert(this->cursorX_++, 1, ' ');
            this->lines_[this->cursorY_].insert(this->cursorX_++, 1, ' ');
            this->lines_[this->cursorY_].insert(this->cursorX_++, 1, ' ');
        } else if (ch == '\b') { // 退格
            if (this->cursorX_ > 0) {
                this->lines_[this->cursorY_].erase(--this->cursorX_, 1);
            } else if (this->cursorY_ > 0) {
                this->cursorX_ = (int)this->lines_[this->cursorY_ - 1].size();
                this->lines_[this->cursorY_ - 1] += this->lines_[this->cursorY_];
                this->lines_.erase(this->lines_.begin() + this->cursorY_);
                this->cursorY_--;
            }
        } else {
            this->lines_[this->cursorY_].insert(this->cursorX_++, 1, ch);
        }

        this->updateScrollBars(hwnd);
        ::InvalidateRect(hwnd, nullptr, TRUE);
    }

    void Editor::handleKeyDown(HWND hwnd, WPARAM wParam) {
        switch (wParam) {
            case VK_LEFT:
                this->cursorX_ = max(0, this->cursorX_ - 1);
                break;
            case VK_RIGHT:
                this->cursorX_ = min((int)this->lines_[this->cursorY_].size(), this->cursorX_ + 1);
                break;
            case VK_UP: {
                if (this->cursorY_ > 0) {
                    this->cursorY_--;
                    this->cursorX_ = min(this->cursorX_, (int)this->lines_[this->cursorY_].size());
                }
                break;
            }
            case VK_DOWN: {
                if (this->cursorY_ < this->lines_.size() - 1) {
                    this->cursorY_++;
                    this->cursorX_ = min(this->cursorX_, (int)this->lines_[this->cursorY_].size());
                }
                break;
            }
        }

        this->cursorVisible_ = true;
        ::InvalidateRect(hwnd, nullptr, TRUE);
    }

    void Editor::updateCurosr(HWND hwnd) {

    }

    void Editor::drawTextLine(HDC hdc, int lineNum, int x, int y) {
        this->synataxHighlight(hdc, this->lines_[lineNum], x, y);
    }

    void Editor::synataxHighlight(HDC hdc, const std::string& line, int x, int y) {
        static const COLORREF colors[] = {
            RGB(91, 91, 91),        // 默认
            RGB(0, 0, 255),         // 关键字
            RGB(163, 21, 21),       // 字符串
            RGB(0, 128, 0),         // 注释
        };

        bool inString = false;
        bool inComment = this->inCommentBlock_;
        std::size_t pos = 0;
        const std::size_t len = line.size();
        
        if (inComment) {
            ::SetTextColor(hdc, colors[3]);
            std::size_t end = line.find("*/");
            if (end != std::string::npos) {
                ::TextOutA(hdc, x, y,
                    line.substr(0, end + 2).c_str(), (int)(end + 2));
                pos = end + 2;
                inComment = false;
            } else {
                ::TextOutA(hdc, x, y,
                    line.c_str(), (int)(len));
                pos = len;
            }
            ::SetTextColor(hdc, colors[0]);
        } 
        
        while (pos < len) {
            if (inComment) {
                std::size_t end = line.find("*/", pos);
                if (end != std::string::npos) {
                    ::TextOutA(hdc, x + (int)pos * this->charWidth_, y,
                        line.substr(pos, end - pos + 2).c_str(), (int)(end - pos + 2));
                    pos = end + 2;
                    inComment = false;
                } else {
                    ::TextOutA(hdc, x + (int)pos * this->charWidth_, y,
                        line.substr(pos).c_str(), (int)(len - pos));
                    pos = len;
                }
                ::SetTextColor(hdc, colors[0]);
            } else if (line[pos] == '"') {
                std::size_t end = line.find('"', pos + 1);
                if (end == std::string::npos) {
                    end = len;
                    inString = true;
                }
                ::SetTextColor(hdc, colors[2]);
                ::TextOutA(hdc, x + (int)pos * this->charWidth_, y,
                    line.substr(pos, end - pos + 1).c_str(), (int)(end - pos + 1));
                pos = end + 1;
                ::SetTextColor(hdc, colors[0]);
            } else if (pos + 1 < len && line[pos] == '/' && line[pos + 1] == '*') {
                ::SetTextColor(hdc, colors[3]);
                std::size_t end = line.find("*/", pos + 2);
                if (end == std::string::npos) {
                    ::TextOutA(hdc, x + (int)pos * this->charWidth_, y,
                        line.substr(pos).c_str(), (int)(len - pos));
                    inComment = true;
                    pos = len;
                } else {
                    ::TextOutA(hdc, x + (int)pos * this->charWidth_, y,
                        line.substr(pos, end - pos + 2).c_str(), (int)(end - pos + 2));
                    pos = end + 2;
                }
                ::SetTextColor(hdc, colors[0]);
            } else if (pos + 1 < len && line[pos] == '/' && line[pos + 1] == '/') {
                ::SetTextColor(hdc, colors[3]);
                ::TextOutA(hdc, x + (int)pos * this->charWidth_, y,
                    line.substr(pos).c_str(), (int)(len - pos));

                pos = len;
                ::SetTextColor(hdc, colors[0]);
            } else {
                size_t wordStart = pos;
                while (pos < len && (isalnum(line[pos]) || line[pos] == '_'))
                    pos++;
                    
                if (pos > wordStart) {
                    std::string word = line.substr(wordStart, pos - wordStart);
                    if (this->keywords_.count(word)) {
                        ::SetTextColor(hdc, colors[1]);
                        ::TextOutA(hdc, x + (int)wordStart * this->charWidth_, y, 
                            word.c_str(), (int)word.size());
                        ::SetTextColor(hdc, colors[0]);
                    } else {
                        ::TextOutA(hdc, x + (int)wordStart * this->charWidth_, y, 
                            word.c_str(), (int)word.size());
                    }
                } else {
                    ::TextOutA(hdc, x + (int)pos * this->charWidth_, y, &line[pos], 1);
                    pos++;
                }
            }
        }
        this->inCommentBlock_ = inComment;
    }

    void Editor::updateScrollBars(HWND hwnd) {
        RECT rc;
        ::GetClientRect(hwnd, &rc);

        SCROLLINFO si = { sizeof(si) };
        si.fMask = SIF_PAGE | SIF_RANGE;

        // 垂直滚动条
        si.nPage = rc.bottom / this->charHeight_;
        si.nMin = 0;
        si.nMax = max(0, (int)this->lines_.size() - 1);
        ::SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

        // 水平滚动条
        int maxWidth = 0;
        for (const auto& line: this->lines_) {
            maxWidth = max(maxWidth, (int)line.length());
        }

        si.nPage = rc.right / this->charWidth_;
        si.nMin = 0;
        si.nMax = max(0, maxWidth - 1);
        ::SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
    }

    int Editor::initFontInfo() {
        // 获取字体信息
        HDC hdc = ::GetDC(_hSelf);
        HFONT hFont = ::CreateFont(16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
            DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS,
            CLEARTYPE_QUALITY, FIXED_PITCH, TEXT("Consolas")); 

        ::SelectObject(hdc, hFont);

        TEXTMETRIC tm;
        ::GetTextMetrics(hdc, &tm);
        charWidth_ = tm.tmAveCharWidth;
        charHeight_ = tm.tmHeight;

        ::DeleteObject(hFont);
        ::ReleaseDC(_hSelf, hdc);

        ::SetTimer(_hSelf, 1, 500, nullptr);

        return 0;
    }
} // namespace edit